﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using snat.model;
using snat.algorithms;

namespace algorithms.algorithms.SimpleLinkPrediction
{
    public class LocalLinkPrediction<T> : IAlgorithm, ITextRenderable
    {

        /*GUI magic*/
        private int ResultsToShow = 24;
        //end of gui magic


        /// <summary>
        /// Holder for the function which shall be used for scoring
        /// </summary>
        private readonly Func<BaseNode, BaseNode, T> scoringFunc;


        /// <summary>
        /// Constructor. Specifies the scoring function that this instance is to use.
        /// </summary>
        /// <param name="_scoringFunc">The scoring function</param>
        public LocalLinkPrediction(Func<BaseNode, BaseNode, T> _scoringFunc) {
            scoringFunc = _scoringFunc;
        }

        #region IAlgorithm Members

        /// <summary>
        /// The algorithm name. Of the form "Link prediction: XXXXX" where XXXXX is replaced
        /// with the name of the scoring function passed in the constructor.
        /// </summary>
        public string Name {
            get { return "Link prediction: " + scoringFunc.Method.Name; }
        }


        /// <summary>
        /// Holds the last scoring of the algorithm. A Dictionary of
        /// KeyValuPair<BaseNode, BaseNode> to a generic type T, typically
        /// an int or double. Updated by calling the Process method.
        /// </summary>
        public Dictionary<KeyValuePair<BaseNode, BaseNode>, T> LastScoring {
            get;
            private set;
        }

        /// <summary>
        /// Convenience method. Takes the results of LastScoring and
        /// outputs a list of KeyValuePairs, ordered by their likelihood
        /// of being an edge.
        /// </summary>
        public List<KeyValuePair<BaseNode, BaseNode>> LastRanking {
            get {
                if (LastScoring != null) {
                    return LastScoring.OrderByDescending(e => e.Value).ToList().ConvertAll(e => e.Key);
                }
                else return null;
            }
        }



        /// <summary>
        /// Registers the algorithm to the tape ended method
        /// </summary>
        public void Register() {
            NetworkModel.Instance.TapeEnded += Process;
        }


        /// <summary>
        /// De-registers the algorithm
        /// </summary>
        public void DeRegister() {
            NetworkModel.Instance.TapeEnded += Process;
        }


        /// <summary>
        /// Performs the ranking with the specified scoring function and updates the LastScoring property.
        /// </summary>
        public void Process() {
            Dictionary<KeyValuePair<BaseNode, BaseNode>, T> scoring = new Dictionary<KeyValuePair<BaseNode, BaseNode>, T>();
            foreach (BaseNode source in NetworkModel.Instance.Nodes.Values) {
                foreach (BaseNode target in NetworkModel.Instance.Nodes.Values) {
                    if (source == target) continue;
                    scoring.Add(new KeyValuePair<BaseNode, BaseNode>(source, target),
                        scoringFunc(source, target));
                }
            }
            LastScoring = scoring;
            if (RenderText != null) RenderText(this);
        }

        #endregion




        #region ITextRenderable Members

        public event RenderTextDelegate RenderText;

        public string GetRenderableText() {
            StringBuilder ret = new StringBuilder();
            ret.AppendLine("\tLINK\t\t\t\tSCORE\t\tSTATUS IN NETWORKMODEL");
            for (int res = 0; res < ResultsToShow; res++) {
                KeyValuePair<BaseNode, BaseNode> newlink = LastRanking[res];
                bool edgeExists = NetworkModel.Instance.Edges.Exists(e=>e.Source == newlink.Key && e.Target == newlink.Value);
                
                ret.AppendFormat(" {0} --> {1} \t {2}\t\t {3}\n", newlink.Key.Name, newlink.Value.Name, LastScoring[newlink],
                edgeExists ? "Link Exists" : "Link does not exist");
            }
            return ret.ToString();
        }

        #endregion
    }
}
